#!/usr/bin/python3
# -*- coding: utf-8 -*-
'''
@Author: Phoenix
@Date: 2020-06-12 12:11:24
Ren'Py翻译文本机器翻译工具

约定：
① 翻译文本首行一定为：'# TODO: Translation updated at xxx'

② 以下形式的代码块为一个“待处理区块（Block Awaiting Processing，简称BAP）”，即“标识符行 + 原文注释 + 译文”。缓存数以一个BAP为基数
translate chinese xxx_xxx_xxxxxxxx:

    # pov "xxxxx" with dissolve
    pov "" with dissolve

③ 文本在循环扫描时，原文say要先于译文say进行判断处理
'''

import os
from re import escape, sub
from sys import exit

import main
from modules.interpreter import Interpreter
from modules.utils import *

_input_abspath = GLOBAL_DATA['rpy_trans_input_abspath']
_output_abspath = ''

# BAP最大缓存量
_bap_max_cache = GLOBAL_DATA['rpy_trans_bap_max_cache']

# 是否覆盖所有译文
_rewrite_all = False

# 是否覆盖TODO译文
_rewrite_todo = False

# 翻译机实例
_interpreter = None


def walk_file():
    '''
    遍历文件夹内所有内容
    '''

    if os.path.isfile(_input_abspath):
        root, f = os.path.split(_input_abspath)
        if not f.endswith('.rpy'):
            return

        # 跳过非翻译文本
        if not is_renpy_translation_file(_input_abspath):
            return

        print(f'当前翻译文本：{f}')
        process_file(_input_abspath, _output_abspath, f)
        return

    for root, dirs, files in os.walk(_input_abspath, topdown=False):

        # 新文件目录
        relative_path = os.path.relpath(root, _input_abspath)
        new_path = (
            _output_abspath
            if relative_path == '.'
            else os.path.join(_output_abspath, relative_path)
        )
        # 若新目录不存在，创建它
        if not os.path.exists(new_path):
            os.makedirs(new_path)

        # 遍历所有文件
        for f in files:
            if not f.endswith('.rpy'):
                # TODO 非翻译文件直接拷贝至新目录
                continue

            in_path = os.path.join(root, f)
            # 跳过非翻译文本
            if not is_renpy_translation_file(in_path):
                # TODO 非翻译文件直接拷贝至新目录
                continue

            print(f'当前翻译文本：{f}')
            process_file(in_path, os.path.join(new_path, f), f)


def process_file(old_path: str, new_path: str, filename: str):
    '''
    读取文本、翻译并写入
    '''

    outp = open(new_path, 'w', encoding=get_file_encoding(new_path))
    inp = open(old_path, 'r', encoding=get_file_encoding(old_path))
    lightSen = inp.readlines()
    inp.close()

    _old_say = ''  # 原文
    _strings_identifier = 'strings'  # strings标识符，用来区分who_say和old_new
    _tmp_lines = ''  # 缓存文本
    _bap_count = 0  # BAP缓存计数

    try:
        for line in lightSen:
            # 空行
            if PATTERN_EMPTY_LINE.match(line) != None:
                _tmp_lines += line
                continue

            # 标志符行
            identifier_match = PATTERN_IDENTIFIER.match(line)
            if identifier_match != None:
                _tmp_lines += line
                _strings_identifier = identifier_match.group(1)
                # 进入新的BAP，缓存计数+1
                _bap_count += 1
                # 扫描到标志符行，说明进入了新的BAP，清空原文
                # 此步非常重要，避免在没有原文且选择覆盖的情况下出错
                _old_say = ''
                continue

            # 原文行
            old_say_match = PATTERN_OLD_SAY.match(line)
            if old_say_match != None and _strings_identifier not in ['', 'strings']:
                _tmp_lines += line
                # 跳过cv语音行
                if old_say_match.group(1) == 'voice':
                    continue

                _old_say = old_say_match.group(2)
                continue

            # 译文行
            new_say_match = PATTERN_NEW_SAY.match(line)
            if new_say_match != None and _strings_identifier not in ['', 'strings']:
                _who = new_say_match.group(1)
                # 跳过cv语音行
                if _who == 'voice':
                    _tmp_lines += line
                    continue

                # 原文为空，直接写入
                if _old_say.strip() == '':
                    _tmp_lines += line
                    if _bap_count >= _bap_max_cache:
                        outp.write(_tmp_lines)
                        outp.flush()
                        _tmp_lines = ''
                        _bap_count = 0
                    continue

                # 如果原文为END_SAY，说明当前BAP已结束，现在是多出来的译文行
                if _old_say == END_SAY:
                    # 如果覆盖所有译文为开，则删除多余的译文行；反之保留
                    if not _rewrite_all:
                        _tmp_lines += line
                        if _bap_count >= _bap_max_cache:
                            outp.write(_tmp_lines)
                            outp.flush()
                            _tmp_lines = ''
                            _bap_count = 0
                    continue

                # 存在字符串形式的who
                who_match = PATTERN_WHO.match(_who)
                if who_match != None and who_match.group(1) != '':
                    who = who_match.group(1)
                    who_new = _interpreter.translate_txt(who, False)
                    if not who_new in MY_ERROR_CODES:
                        _who = sub(escape(repr(who))[1:-1], who_new, _who)

                original_new_say = new_say_match.group(2)
                with_transition = new_say_match.group(3)
                # 当译文不为空时，根据覆盖写入开关进行相应操作
                if original_new_say != '':
                    # 当覆盖所有译文为开,或者译文为TODO，或者覆盖TODO译文为开，且已有译文开头为TODO时，发出翻译请求
                    if (
                        _rewrite_all
                        or original_new_say.upper() == TODO_MARK
                        or (
                            _rewrite_todo
                            and original_new_say.upper().startswith(TODO_MARK)
                        )
                    ):
                        new_say = _interpreter.translate_txt(_old_say)
                        # 如果翻译错误，则保持原样
                        if new_say in MY_ERROR_CODES:
                            _tmp_lines += line
                        else:
                            _tmp_lines += (
                                '    '
                                + ((_who + ' ') if _who != '' else _who)
                                + '\"'
                                + new_say
                                + '\"'
                                + (
                                    (' ' + with_transition)
                                    if with_transition != ''
                                    else with_transition
                                )
                                + '\n'
                            )
                    else:
                        _tmp_lines += line
                else:
                    new_say = _interpreter.translate_txt(_old_say)
                    # 如果翻译错误，则保持原样
                    if new_say in MY_ERROR_CODES:
                        _tmp_lines += line
                    else:
                        _tmp_lines += (
                            '    '
                            + ((_who + ' ') if _who != '' else _who)
                            + '\"'
                            + new_say
                            + '\"'
                            + (
                                (' ' + with_transition)
                                if with_transition != ''
                                else with_transition
                            )
                            + '\n'
                        )
                if _bap_count >= _bap_max_cache:
                    outp.write(_tmp_lines)
                    outp.flush()
                    _tmp_lines = ''
                    _bap_count = 0
                # 当前BAP处理结束，若当前BAP的译文是多行翻译，除第一行外均删除
                _old_say = END_SAY
                continue

            # old行
            old_match = PATTERN_OLD.match(line)
            if old_match != None and _strings_identifier == 'strings':
                _tmp_lines += line
                _old_say = old_match.group(1)
                continue

            # new行
            new_match = PATTERN_NEW.match(line)
            if new_match != None and _strings_identifier == 'strings':
                if _old_say == '':
                    _tmp_lines += line
                    if _bap_count >= _bap_max_cache:
                        outp.write(_tmp_lines)
                        outp.flush()
                        _tmp_lines = ''
                        _bap_count = 0
                    continue

                original_new = new_match.group(1)  # 译文
                # 当译文不为空时，根据覆盖写入开关进行相应操作
                if original_new != '':
                    # 当覆盖所有译文为开,或者译文为TODO，或者覆盖TODO译文为开，且已有译文开头为TODO时，发出翻译请求
                    if (
                        _rewrite_all
                        or original_new.upper() == TODO_MARK
                        or (
                            _rewrite_todo and original_new.upper().startswith(TODO_MARK)
                        )
                    ):
                        new = _interpreter.translate_txt(_old_say)
                        # 如果翻译错误，则保持原样
                        if new in MY_ERROR_CODES:
                            _tmp_lines += line
                        else:
                            _tmp_lines += '    new \"' + new + '\"\n'
                    else:
                        _tmp_lines += line
                else:
                    new = _interpreter.translate_txt(_old_say)
                    if new in MY_ERROR_CODES:
                        _tmp_lines += line
                    else:
                        _tmp_lines += '    new \"' + new + '\"\n'
                if _bap_count >= _bap_max_cache:
                    outp.write(_tmp_lines)
                    outp.flush()
                    _tmp_lines = ''
                    _bap_count = 0
                _old_say = ''
                continue

            # 其他行
            _tmp_lines += line
    except Exception as e:
        print_err(
            f'数据读写异常：{e.args[0]}，当前标识符：{_strings_identifier}！'
        )
        print_err(f'{filename} 翻译终止！\n')
    else:
        if _tmp_lines != '':
            outp.write(_tmp_lines)
        print_info(f'{filename} 翻译完成！\n')
    finally:
        outp.close()


def input_path(reselect='0'):
    '''
    输入待翻文件/文件夹的绝对路径
    '''

    global _input_abspath, _output_abspath

    if reselect == '0':
        if _input_abspath != '':
            # 若路径不存在，则重新手动输入
            if not os.path.exists(_input_abspath):
                print_err('config.ini配置的翻译文本路径不存在！\n')
                _input_abspath = ''
                input_path()
                return

            if os.path.isdir(_input_abspath):
                _output_abspath = _input_abspath + '-new'
                # 如果输出文件夹已存在，先将其更名，再新建空文件夹
                if os.path.exists(_output_abspath):
                    os.rename(
                        _output_abspath,
                        _output_abspath
                        + '_'
                        + datetime.now().strftime('%Y_%m_%d_%H_%M'),
                    )
                os.makedirs(_output_abspath)
            elif os.path.isfile(_input_abspath):
                _inp = os.path.splitext(_input_abspath)
                _output_abspath = _inp[0] + '-new' + _inp[-1]
                # 如果输出文件已存在，将其更名备份
                if os.path.exists(_output_abspath):
                    (
                        os.rename(
                            _inp[0] + '-new',
                            _inp[0]
                            + '-new_'
                            + datetime.now().strftime('%Y_%m_%d_%H_%M'),
                        )
                        + _inp[-1]
                    )
            print_info('路径验证成功！\n')
            return

        inp = input('请输入翻译文本的绝对路径：').strip()
        # 输入为空，重新输入
        if inp == '':
            input_path('1')
            return
        # 规范路径，不调整大小写
        inp = os.path.normpath(inp)
        # 若路径不存在，重新输入
        if not os.path.exists(inp):
            input_path('1')
            return

        _input_abspath = inp
        if os.path.isdir(_input_abspath):
            _output_abspath = _input_abspath + '-new'
            # 如果输出文件夹已存在，先将其更名，再新建空文件夹
            if os.path.exists(_output_abspath):
                os.rename(
                    _output_abspath,
                    _output_abspath + '_' + datetime.now().strftime('%Y_%m_%d_%H_%M'),
                )
            os.makedirs(_output_abspath)
        else:
            _inp = os.path.splitext(_input_abspath)
            _output_abspath = _inp[0] + '-new' + _inp[-1]
            # 如果输出文件已存在，将其更名备份
            if os.path.exists(_output_abspath):
                (
                    os.rename(
                        _inp[0] + '-new',
                        _inp[0] + '-new_' + datetime.now().strftime('%Y_%m_%d_%H_%M'),
                    )
                    + _inp[-1]
                )
        print_info('路径验证成功！\n')
        return

    tmp = input('路径错误，请重新输入正确的路径或回车关闭程序：').strip()
    # 输入为空，退出程序
    if tmp == '':
        exit(0)
    # 规范路径，不调整大小写
    tmp = os.path.normpath(tmp)
    # 若路径不存在，重新输入
    if not os.path.exists(tmp):
        input_path('1')
        return

    _input_abspath = tmp
    if os.path.isdir(_input_abspath):
        _output_abspath = _input_abspath + '-new'
        # 如果输出文件夹已存在，先将其更名，再新建空文件夹
        if os.path.exists(_output_abspath):
            os.rename(
                _output_abspath,
                _output_abspath + '_' + datetime.now().strftime('%Y_%m_%d_%H_%M'),
            )
        os.makedirs(_output_abspath)
    else:
        _inp = os.path.splitext(_input_abspath)
        _output_abspath = _inp[0] + '-new' + _inp[-1]
        # 如果输出文件已存在，将其更名备份
        if os.path.exists(_output_abspath):
            (
                os.rename(
                    _inp[0] + '-new',
                    _inp[0] + '-new_' + datetime.now().strftime('%Y_%m_%d_%H_%M'),
                )
                + _inp[-1]
            )
    print_info('路径验证成功！\n')


def is_rewrite_all() -> bool:
    '''
    是否覆盖所有译文。如果提取翻译文本未勾选“为翻译生成空字串”，则务必选择覆盖写入
    '''

    print('\n！！！！！以下选项谨慎操作！！！！！')
    rewrite_tmp = input(
        '是否覆盖所有译文？输入“y”覆盖，输入其他内容不覆盖。\n注意：如果生成翻译文本未勾选“为翻译生成空字串”，则必须选择覆盖：'
    ).strip()

    if rewrite_tmp in ['Y', 'y']:
        print('=====================当前选择为：覆盖写入=====================\n')
        return True

    print('====================当前选择为：不覆盖写入====================\n')
    return False


def is_rewrite_todo() -> bool:
    '''
    是否覆盖TODO译文
    '''

    print('\n！！！！！以下选项谨慎操作！！！！！')
    rewrite_tmp = input('是否覆盖TODO译文？输入“y”覆盖，输入其他内容不覆盖：').strip()

    if rewrite_tmp in ['Y', 'y']:
        print('=====================当前选择为：覆盖写入=====================\n')
        return True

    print('====================当前选择为：不覆盖写入====================\n')
    return False


def _select_serial_num(reselect=False, serial_num=''):
    '''
    输入序号选择对应的操作
    '''

    if not reselect:
        print(
            '''1) 翻译文本
0) 返回上一级
'''
        )

        _inp = input('请输入要操作的序号：').strip()
        if _inp == '1':
            return
        if _inp == '0':
            main.start_main()
        else:
            _select_serial_num(True, _inp)
        return

    _tmp = input(
        f'列表中不存在序号 {serial_num}，请重新输入正确序号或回车退出程序：'
    ).strip()
    if _tmp == '':
        exit(0)

    if _tmp == '1':
        return
    if _tmp == '0':
        main.start_main()
    else:
        _select_serial_num(True, _tmp)


def start():
    '''
    翻译文本模式
    '''

    print(
        r'''
======================================================================
                        Ren'Py 翻译文本机翻工具
                            作者：Phoenix
                            版权归作者所有
                  PS：本工具所有操作均不会影响原文件！
======================================================================
'''
    )

    _select_serial_num()

    input_path()

    global _interpreter, _rewrite_all, _rewrite_todo

    _interpreter = Interpreter()
    # 配置翻译机
    _interpreter.configure_tool()

    # 是否开启覆盖所有译文
    _rewrite_all = is_rewrite_all()

    # 如果不覆盖所有译文，再询问是否开启覆盖TODO译文
    if not _rewrite_all:
        _rewrite_todo = is_rewrite_todo()

    walk_file()
    print_info('翻译已全部完成，请前往原路径查看翻译文本！')
    exit(0)
